///\todo upgrade

//////////////////////////////////////////////////////////////////////////////////
//                                                                              //
//  This file is part of the buola project (https://code.google.com/p/buola/).  //
//                                                                              //
//  Copyright(c) 2007-2012 Xavi Gratal                                          //
//  gratal AT gmail DOT com                                                     //
//                                                                              //
//  Buola is free software: you can redistribute it and/or modify               //
//  it under the terms of the GNU General Public License as published by        //
//  the Free Software Foundation, either version 3 of the License, or           //
//  (at your option) any later version.                                         //
//                                                                              //
//  Buola is distributed in the hope that it will be useful,                    //
//  but WITHOUT ANY WARRANTY; without even the implied warranty of              //
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               //
//  GNU General Public License for more details.                                //
//                                                                              //
//  You should have received a copy of the GNU General Public License           //
//  along with buola.  If not, see <http://www.gnu.org/licenses/>.              //
//                                                                              //
//////////////////////////////////////////////////////////////////////////////////

#ifndef _BUOLA_IMAGE_ALGORITHM_H_
#define _BUOLA_IMAGE_ALGORITHM_H_

#include <buola/image.h>
#include <buola/image/cmview.h>
#include <buola/image/ccolorspaceconversion.h>
#include <buola/image/traits.h>
#include <array>

namespace buola { namespace img {

DECLARE_NAME(Clamp)
    
///Copies pixels between two views with the same format.

///It requires both views to be of the same size
template<typename tFormat>
void copy_pixels(const CView<tFormat,1> &pSrc,const CMView<tFormat,1> &pDst)
{
    if(pSrc==pDst) return;
    
    assert(pSrc.Size()==pDst.Size());
    //we can't tell subimages from aligned images, so we can't do the direct copy unless the strides are equal and
    //the image is packed
    if(pSrc.Stride()==pDst.Stride()&&pSrc.Continuous())
    {
        memcpy(pDst.RawData(),pSrc.RawData(),pSrc.Height()*pSrc.Stride());
    }
    else
    {
        for(int i=0;i<pSrc.Height();i++)
        {
            memcpy(pDst.Row(i).RawData(),pSrc.Row(i).RawData(),pSrc.Width()*tFormat::mPixelSize);
        }
    }
}

template<typename tFormat,int vAdvance1,int vAdvance2>
void copy_pixels(const CView<tFormat,vAdvance1> &pSrc,const CMView<tFormat,vAdvance2> &pDst)
{
    assert(pSrc.Size()==pDst.Size());

    static const std::size_t lChannels=tFormat::TColorSpace::cChannels;
    typedef typename tFormat::TChannel::TType tType;
    for(int y=0;y<pSrc.Height();y++)
    {
        auto lS=pSrc.Row(y).begin();
        auto lD=pDst.Row(y).begin();
        
        for(int x=0;x<pSrc.Width();x++)
        {
            for(int c=0;c<lChannels;c++)
                lD[x][c]=(tType)lS[x][c];
        }
    }
}

///Copies pixels between two views with the same colorspace

///No channel conversion is performed, so the result is just the result of casting each of the pixel values into
///the destination view.
///
///This function has the same behaviour as reinterpret_pixels, but it can only be applied to views with the same
///colorspace

template<typename tSrc>
void cast_pixels(const CView<tSrc> &pSrc,const CMView<tSrc> &pDst)
{
    copy_pixels(pSrc,pDst);
}

template<typename tSrc,typename tDst>
typename std::enable_if<std::is_same<typename tSrc::TColorSpace,typename tDst::TColorSpace>::value,void>::type
cast_pixels(const CView<tSrc> &pSrc,const CMView<tDst> &pDst)
{
    static const std::size_t lChannels=tDst::TColorSpace::cChannels;
    typedef typename tDst::TChannel::TType tType;
    for(int y=0;y<pSrc.Height();y++)
    {
        auto lS=pSrc.RowBegin(y);
        auto lD=pDst.RowBegin(y);
        
        for(int x=0;x<pSrc.Width();x++)
        {
            for(int c=0;c<lChannels;c++)
                lD[x][c]=(tType)lS[x][c];
        }
    }
}

template<typename tSrc,typename tDst>
typename std::enable_if<std::is_same<typename tSrc::TColorSpace,typename tDst::TColorSpace>::value,void>::type
cast_pixels(const CView<tSrc> &pSrc,const CMView<tDst> &pDst,NClamp)
{
    static const std::size_t lChannels=tDst::TColorSpace::cChannels;
    typedef typename tDst::TChannel::TType TType;
    typedef typename tDst::TChannel::TWorkingType TWorkingType;
    for(int y=0;y<pSrc.Height();y++)
    {
        auto lS=pSrc.RowBegin(y);
        auto lD=pDst.RowBegin(y);
        
        for(int x=0;x<pSrc.Width();x++)
        {
            for(int c=0;c<lChannels;c++)
                lD[x][c]=(TType)clamp((TWorkingType)lS[x][c],
                                      (TWorkingType)tDst::TChannel::mMin,(TWorkingType)tDst::TChannel::mMax);
        }
    }
}

///Converts pixels between two views

///This function performs channel and colorspace conversion, so the views can be of any type, as long as the
///conversion between the two colorspaces and two channels is defined

template<typename tFmt>
void convert_pixels(const CView<tFmt> &pSrc,const CMView<tFmt> &pDst)
{
    copy_pixels(pSrc,pDst);
}

template<typename tSrc,typename tDst>
void convert_pixels(const CView<tSrc> &pSrc,const CMView<tDst> &pDst)
{
    CColorSpaceConversion<typename tSrc::TColorSpace,typename tDst::TColorSpace> lConversion;

    for(int y=0;y<pSrc.Height();y++)
    {
        auto lS=pSrc.Row(y).begin();
        auto lD=pDst.Row(y).begin();
        
        for(int x=0;x<pSrc.Width();x++)
        {
            lConversion(lS[x],lD[x]);
        }
    }
}

///Converts a single pixel

///This function is just a single pixel version of convert_pixels
///
///It performs channel and colorspace conversion, so they can be of any type, as long as the
///conversion between the two colorspaces and two channels is defined

template<typename tFmt>
void convert_pixel(const CPixel<tFmt> &pSrc,CPixel<tFmt> &pDst)
{
    pDst=pSrc;
}

template<typename tSrc,typename tDst>
void convert_pixel(const CPixel<tSrc> &pSrc,CPixel<tDst> &pDst)
{
    CColorSpaceConversion<typename tSrc::TColorSpace,typename tDst::TColorSpace> lConversion;
    lConversion(pSrc,pDst);
}

///Fills an image with a constant pixel value
template<typename tFormat>
void fill_pixels(const CMView<tFormat> &pI,const CPixel<tFormat> &pPixel)
{
    for(int y=0;y<pI.Height();y++)
    {
        auto lI=pI.Row(y).begin();
        
        for(int x=0;x<pI.Width();x++)
            lI[x]=pPixel;
    }
}

///Fills an image with zero pixels
template<typename tFormat>
void zero_pixels(const CMView<tFormat> &pI)
{
    //we can't tell subimages from aligned images, so we can't do the direct copy unless the strides are equal and
    //the image is packed
    if(pI.Continuous())
    {
        memset(pI.RawData(),0,pI.Height()*pI.Stride());
    }
    else
    {
        for(int i=0;i<pI.Height();i++)
            memset(pI.RawData(i),0,pI.Width()*tFormat::mPixelSize);
    }
}


/*namespace image*/ } /*namespace buola*/ }

#endif
